<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>观察者模式</title>
</head>

<body>
    <script>
        /*观察者模式
        例子：监控
        => 我们坐在教室离就是被观察者
        => 监控后面的老师，就是观察者
        => 当被观察者触发了一些条件的时候，观察者就会触发技能
        观察者模式：监控一个对象的状态，一旦状态发生变化，马上触发技能
        =>需要两个构造函数来实现
           1.创建被观察者
            =>属性，自己的状态
            =>队列，记录着都有谁观察着自己,[]
            =>方法，能设置自己的状态，当我需要改变的时候，需要触发这个方法改变状态
            =>方法，添加观察者
            =>方法，删除观察者
           2.创建观察者
           =>需要一个身份证明
           =>需要一个技能    

        */

        // 观察者构造函数
        class Observer {
            constructor(name, fn = () => { }) {
                this.name = name
                this.fn = fn
            }
        }
        //创建两个观察者
        const bzr = new Observer('班主任', (state) => { console.log('因为：' + state + ',把你爸找来！') })
        const xz = new Observer('校长', (state) => { console.log('因为:' + state + ',骂你的班主任') })
        const njzr = new Observer('年级主任', (state) => { console.log('因为:' + state + ',你是哪个班的？') })

        //被观察者的构造函数
        class Subject {
            constructor(state) {
                //记录自己的状态
                this.state = state
                //数组用来记录保存观察我的人
                this.observers = []

            }
            //设置自己的状态
            setState(val) {
                this.state = val
                //就需要把我所有观察者的技能都触发
                //遍历this.observers 一次出发
                this.observers.forEach(item => {
                    // item 就是每一個观察者，就是一个一个的对象
                    // console.log(item, 'item');
                    // 把每一个观察者的技能触发
                    // item.fn()
                    //告诉他我改变成了什么状态
                    item.fn(this.state)
                })
            }
            //添加观察者
            addObserver(obs) {
                //谁是观察者，就传递过来
                //如果观察者已经存在，就不在添加了
                //1.some()
                // const res = this.observers.some(item => item === obs)
                //2.find()
                // const res = this.observers.find(item => item === obs)
                //3.indexOf()
                // const res = this.observers.indexOf(obs)
                //4.filter()
                // const res = this.observers.filter(item => item === obs)
                //5.filter() 创建一个新数组，检测数组元素，并返回所有符合条件的元素的组数-不改变原数组
                // const res = this.observers.filter(item => item === obs)
                // console.log(res)
                this.observers = this.observers.filter(item => item !== obs) //更新observers
                //6.findIndex() 返回符合传入测试（函数）条件的数组元素索引(元素位置)-不改变原数组
                // const res = this.observers.findIndex(item => item === obs)//和indexOf是一个道理
                // console.log(res);
                this.observers.push(obs)
            }
            // 删除观察者
            delObserver(obs) {
                // 吧观察者删除
                // 直接使用filter方法
                this.observers = this.observers.filter(item => item !== obs)
            }
        }
        //创造一个被观察者
        const xiaoming = new Subject('学习')
        // 控制台：xiaoming.setState("玩耍"); console.log(xiaoming)
        //给小明添加一个观察者
        xiaoming.addObserver(bzr)
        xiaoming.addObserver(xz)
        xiaoming.addObserver(njzr)
        const xiaohong = new Subject("学习")
        xiaohong.addObserver(bzr)
        xiaohong.addObserver(xz)
        console.log(xiaoming)//打印被观察xiaoming 
        console.log(xiaohong)//打印被观察xiaoming 
    </script>
</body>

</html>